package com.itdfq.springaop.demo;


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import java.util.Arrays;

/**
 * @Author: QianMo
 * @Date: 2021/11/17 10:20
 * @Description:
 */
@Aspect
public class LogAspects {
    /**
     * 切入点
     *
     *
     *
     */
    @Pointcut("execution(int com.itdfq.springaop.demo.MathCalculator.div(int,int))")
    public void pointCut() {
    }


    /**
     * JoinPoint
     *
     * Object[] getArgs()：返回执行目标方法时的参数
     * Signature getSignature():返回被增强方法的相关信息，
     * Object getTarget():返回被织入增强处理的目标对象
     * Object getThis():返回AOP框架目标对象生成的代理对象
     *
     * -----------------------------
     * ProceedingJoinPoint
     * Object proceed():执行此方法才会执行目标方法
     * Object proceed(Object[] args):执行此方法才会执行目标方法，而且会使用Object数组参数去代替实参,
     * 如果传入Object[]参数与方法入参数量不同或类型不同，会抛出异常
     */



    /**
     * 前置
     * 在切点方法之前执行
     */
    @Before("com.itdfq.springaop.demo.LogAspects.pointCut()")
    public void logStart(JoinPoint joinPoint) {
        System.out.println(joinPoint.toString());
        System.out.println("前置通知：" + joinPoint.getSignature().getName() + " 除法运行,参数是：" + Arrays.asList(joinPoint.getArgs()));
    }

    /**
     * 后置
     * 在切点方法之后执行
     *
     * @After不管结果如何都会被织入
     */
    @After(value = "com.itdfq.springaop.demo.LogAspects.pointCut()")
    public void logEnd() {
        System.out.println("后置通知：除法结束");
    }

    /**
     * 返回通知
     * <p>
     *  在方法返回之前，获取返回值，并记录
     *
     *  returning定义的参数名作为切面方法的入参名，类型可以指定。如果切面方法入参类型指定Object则无限制，
     *  如果为其它类型，则当且仅当目标方法返回相同类型时才会进入切面方法，否则不会
     * 只有方法成功完成才会被织入
     *
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "com.itdfq.springaop.demo.LogAspects.pointCut()", returning = "result")
    public void logReturn2(JoinPoint joinPoint, Object result) {
        System.out.println("返回通知：" + joinPoint.getSignature().getName() + "除法返回" + result);
    }

    /**
     * 异常
     *
     * @param exception
     * @After只有异常出现才会被织入
     */
    @AfterThrowing(value = "com.itdfq.springaop.demo.LogAspects.pointCut()", throwing = "exception")
    public void logException(Exception exception) {
        System.out.println("异常通知：除法异常");
    }

    /**
     * 环绕通知
     *
     * @param pjp 返回参数需要与鉴定的方法返回结果保持一致
     * @return
     * @Around相当于@Before和@AfterReturning功能的总和 <p/>
     */
    @Around(value = "com.itdfq.springaop.demo.LogAspects.pointCut()")
    public Integer around(ProceedingJoinPoint pjp) {
        System.out.println("环绕通知:" + pjp.getSignature().getName());

        //需要执行ProceedingJoinPoint对象的proceed方法，在这个方法前与后面做环绕处理，可以决定何时执行与完全阻止方法的执行
        try {
            System.out.println("事务开启");
            Object proceed = pjp.proceed();
            System.out.println("事务关闭");
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return 124;

        /**
         * 关闭pjp.proceed();  其他的通知不会执行
         * <p/>
         * 环绕通知:div
         * 事务开启
         * 事务关闭
         * 124
         * </p>
         * 开启之后
         * 环绕通知:div
         * 事务开启
         * execution(int com.itdfq.springaop.demo.MathCalculator.div(int,int))
         * 前置通知：div 除法运行,参数是：[4, 2]
         * 2
         * 返回通知：div除法返回2
         * 后置通知：除法结束
         * 事务关闭
         * 124
         */
    }

}
